<?php

// See GMLP.TXT, etc. The main processing code does not support setext markup 
// or link references. See comments at the end of gmlp_convert().

// Version 1.3, July 23, 2014

$gmlp_translate['STRIP'] = '';		// mixed; $replace in str_replace()
$gmlp_translate['NEWLINE'] = "\n";	// delimiter for explode()

$gmlp_translate['EOL'] = array('','<p>','</p>',"\n");
define('EOL_BR',0);
define('EOL_P1',1);
define('EOL_P2',2);
define('EOL_NL',3);

$gmlp_translate['OPT'] = array(
'/G' => 1,				// greedy paragraphing
);

$gmlp_translate['lines'] = array(
'/^######\s*([^#]*)([#]*)/' => '<h6>$1</h6>',
'/^#####\s*([^#]*)([#]*)/' => '<h5>$1</h5>',
'/^####\s*([^#]*)([#]*)/' => '<h4>$1</h4>',
'/^###\s*([^#]*)([#]*)/' => '<h3>$1</h3>',
'/^##\s*([^#]*)([#]*)/' => '<h2>$1</h2>',
'/^#\s*([^#]*)([#]*)/' => '<h1>$1</h1>',
'/^\-{3,}/' => '<hr>',
'/^\-+ +\-+ +/' => '<hr>',
'/^\*{3,}/' => '<hr>',
'/^\*+ +\*+ +/' => '<hr>',
'/^``(.*)``/' => '<p><code>$1</code></p>',
);

// applied to lines making up a paragraph and to block lines
$gmlp_translate['line'] = array(
'/ {2,}$/' => '<br>',
'/^(\s{4}|\t)(.*)/' => '$2',
);

$gmlp_translate['inlines'] = array(
'/<(?![a-z\/?])/' => '&lt;',				// from Markdown.pl
'/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/' => '&amp;',	// "

'/\\\\\*/U' => '&#042;',	// to allow "\* and *bold*"
'/\\\\_/U' => '&#095;',		// both will break inside `` unfortuately
# yeah; need work...

'/``\s+(.*)\s+``/U' => 'md_code_str',
'/`(.*)`/U' => 'md_code_str',

'/!\[(.*)\]\((\S*)\s*\"(.*)\"\)/U' => '<img src="$2" title="$3" alt="$1">',
'/!\[(.*)\]\((.*)\)/U' => '<img src="$2" alt="$1">',

'/\[(.*)\]\(\/(\S*)\/\s*(\".*\")\)/U' => '<a href="$2" title=$3>$1</a>',
'/\[(.*)\]\(\/(\S*)\/\s*\"([^\"]*)\"\)/U' => '<a href="$2" title="$3">$1</a>',
'/\[(.*)\]\(<(\S*)>\s*\"([^\"]*)\"\)/U' => '<a href="$2" title="$3">$1</a>',
'/\[(.*)\]\((\S*)\s*\"([^\"]*)\"\)/U' => '<a href="$2" title="$3">$1</a>',

# don't know about this one...
//'/\[(.*)\]\(\/(.*)\/\)/U' => '<a href="$2">$1</a>',
'/\[(.*)\]\(<(.*)>\)/U' => '<a href="$2">$1</a>',
'/\[(.*)\]\((.*)\)/U' => '<a href="$2">$1</a>',

'/<(http[s]*\S*)>/U' => '<a href="$1">$1</a>',

'/\\\\\\\\/U' => '\\',
'/\\\\!/U' => '!',
'/\\\\\[/U' => '[',
'/\\\\\(/U' => '(',
'/\\\\#/U' => '#',
'/\\\\`/U' => '`',
'/\\\\\./U' => '.',

);

// needs strenuous testing
$gmlp_translate['wordbeg'] = '/(^|\s*)';
$gmlp_translate['wordend'] = '(\s*|$)/U';

$gmlp_translate['chars'] = array(
'**' => 'strong',
'__' => 'strong',
'*' => 'em',
'_' => 'em',
);

$gmlp_translate['blocks']['leadingspace'] = array(
'begin' => '/^\s{4}|^\t/',
'end' => '/(^\S+|^$)/',
'pre' => '<pre><code>',
'replace' => 'htmlentities',
'post' => '</code></pre>',
'first' => 1,
'continue' => 1,
);

$gmlp_translate['blocks']['ulist'] = array(
'begin' => '/^(\*|\+|\-)\s+/',
'function' => 'md_convert_list',
'continue' => 1,
);

$gmlp_translate['blocks']['olist'] = array(
'begin' => '/^[0-9]+\.\s+/',
'function' => 'md_convert_list',
'continue' => 1,
);

$gmlp_translate['blocks']['quote'] = array(
'begin' => '/^>/',
'end' => '/(^[^>]|^$)/',
'first' => 1,
'post' => 'md_blockquote',
'continue' => 1,
);

$gmlp_translate['blocks']['html'] = array(
'begin' => '/^<[a-z]+>/',
'end' => '/^<\/[a-z]+>/',
'first' => 1,
'last' => 1,
);


function md_code_str($s) {
	$s = htmlentities($s);
	$s = str_replace('`','&#096;',$s);
	return "<code>$s</code>";
}

// parse a block of lines as a string

function md_blockquote($str) {

	$s = '<p>';
	$a = explode('>',$str); array_shift($a);
	foreach ($a as $_) {
		$_ = trim($_);
		if ($_ === '')
			$s .= "</p><p>";
		else {
			gmlp_convert_str($_);
			$s .= "$_ ";
		}
	}
	$s .= '</p>';

	return "<blockquote>$s</blockquote>";
}

// parse a block of lines as an array (slice off of input array)

function md_convert_list(&$data, &$s, $re) {

# first draft (this is a *terrible* function to be replaced)...
# see Textile list code for other ways to do this

	$end = '/^\S+/';
	$beg = $re['begin'];
	$tag = array("<ul>\n","</ul>\n");
	if ($beg == '/^[0-9]+\.\s+/')
		$tag = array("<ol>\n","</ol>\n");

	$block[] = $s;

	// gather entire block
	while (($_ = array_shift($data)) !== NULL) {
		if (preg_match($end,$_) && !preg_match($beg,$_))
			break;
		$block[] = $_;
	}
	array_unshift($data,$_);

	$c = 0;
	$s = $tag[0];
	foreach ($block as $_) {
# this is where nested lists would be tracked (somehow TBD)...
		if (preg_match($beg,$_)) {
			if ($c) $s .= "</p></li>\n";
			$_ = preg_replace($beg,'',$_);
			gmlp_convert_str($_);
			$s .= "<li><p>".preg_replace($beg,'',$_.' ');
			$c = 1;
		}
		else {
			$_ = preg_replace('/^\t/','    ',$_);
			$_ = preg_replace('/^\s{4,4}/','',$_);
			gmlp_convert_str($_);
			$s .= ($_ == '') ? "</p><p>": "$_ ";
		}
	}
	$s .= $tag[1];
}

/* function hooks */

// access list for them
$gmlp_translate['HOOKS'] = array(
'convert' => 'md_convert',
'lines' => 'md_lines',
);

// 'convert' called start of conversion *after* input text stripped, exploded

function md_convert(&$data) {

// create list of reference links (and delete them); setext headers

	$ref = '/^\[(.*)\]:\s*(\S*)\s*(.*)/';
	$n = count($data) - 1;
	for ($i = 0; $i < $n; $i++) {
		$line = $data[$i]; 
		if (preg_match($ref,$line,$res)) {
			list(,$id,$link,$title) = $res;
# a more elaborate RE might be able to provide for this:
			$title = substr($title,1,-1);
			// store in nice format
			$rl = array($link,$title);
			md_data('refs',$id,$rl);
			$data[$i] = '';
		}
		else
		if ($i && preg_match('/^=+$/',$line) && $data[$i-1]) {
			$data[$i] = '';
			$data[$i-1] = '# '.$data[$i-1];
		}
		else
		if ($i && preg_match('/^-+$/',$line) && $data[$i-1]) {
			$data[$i] = '';
			$data[$i-1] = '## '.$data[$i-1];
		}
# you can see why markup designed to look forward and back is complcated 
# when parsing input text *one line at a time* (which is why it did not occur 
# to me when I started) -- however, so far, the input text is iterated over 
# only twice (here and then by gmlp_convert()) so this is not too bad (and 
# it is still only 300 lines here, 400 lines of main code)
	}

// what the reference link data looks like (see also md_lines()):
// var_dump(md_data('refs'));
// array(1) { ["ref"]=> array(2) { [0]=> string(19) "http://example.com/" [1]=> string(21) "Optional Title Here" } } 
}

// 'lines' called for each line of input text before all other conversions

function md_lines(&$line) {
static $refs = array();

// currently only replaces reference links with store references
// img links can be done the same way...

	if ($refs == array())
		$refs = md_data('refs');	// do this just once

	$ref = '/\[(.*)\][ ]?\[(.*)\]/U';
	if ($n=preg_match_all($ref,$line,$res)) {
		$a = $res[1];			// array of a texts
		$id = $res[2];			// array of id names
		for ($i = 0; $i < $n; $i++) {
			if (!isset($refs[$id[$i]]))
				continue;
			list($link,$title) = $refs[$id[$i]];
			$s = "<a href='$link' title='$title'>{$a[$i]}</a>";
			$line = preg_replace($ref,$s,$line);
		}
	}
}

/* example data storage - write/read variables */

function md_data($var = NULL, $val = NULL, $dat = NULL) {
static $vars = array();

	if ($var == NULL)
		return $vars;
	if ($val !== NULL) {
		// associative array
		if ($dat !== NULL)
			return $vars[$var][$val] = $dat;
		// simple array
		if (isset($vars[$var]) && is_array($vars[$var]))
			return $vars[$var][] = $val;
		// simple variable
		return $vars[$var] = $val;
	}

	return (isset($vars[$var])) ? $vars[$var] : NULL;
}

return 1;
